export function castArray<T>(val: T | T[]) {
  return Array.isArray(val) ? val : val == null ? [] : [val]
}

export function get(obj: Record<string, any>, path: string | Array<string | number>) {
  if (!obj) return obj
  if (!path) return void 0
  if (typeof path === 'string') path = path.split('.')
  for (let i = 0, len = path.length; i < len; i++) {
    if ((obj = obj[path[i]]) == null) break
  }
  return obj
}

export function set(obj: Record<string, any>, path: string | Array<string | number>, val) {
  if (!obj) return obj
  if (!path) return obj
  if (typeof path === 'string') path = path.split('.')
  let temp = obj
  for (var i = 0, len = path.length - 1; i < len; i++) {
    temp = temp[path[i]] ?? (temp[path[i]] = {})
  }
  temp[path[i]] = val
  return obj
}

export function property(path) {
  return obj => get(obj, path)
}

export function keyBy(arr: Array<any>, iteratee: string | ((item: any) => string)): object {
  if (typeof iteratee !== 'function') iteratee = property(iteratee)
  // @ts-ignore
  return arr.reduce((obj, item) => ((obj[iteratee(item)] = item), obj), {})
}

export function omit<T, K extends keyof T>(obj: T, path: K | K[]): Omit<T, K> {
  path = castArray(path) as K[]
  const temp = { ...obj }
  path.forEach(key => {
    delete temp[key]
  })
  return temp
}

export function cloneDeep(obj) {
  if (obj == null) return obj
  if (obj instanceof Date) return new Date(obj)
  if (obj instanceof RegExp) return new RegExp(obj)
  if (typeof obj !== 'object') return obj
  // 接下来，要么是对象，要么是数组 可以用 new obj.constructor得到它类型的空值
  let cloneObj = new obj.constructor()
  for (const key in obj) {
    if (obj.hasOwnProperty(key)) {
      cloneObj[key] = cloneDeep(obj[key])
    }
  }
  return cloneObj
}

export function throttle<T extends Function>(fn: T, wait): T & { cancel() } {
  let t1: number,
    t2: number,
    args = [],
    timer: NodeJS.Timeout
  const fun = (..._args) => {
    args = _args
    t1 = Date.now()
    if (t1 >= t2 + wait) {
      fn.apply(null, args)
      t2 = t1
    } else {
      if (timer) return
      timer = setTimeout(() => {
        fn.apply(null, args)
        t2 = Date.now()
        timer = null
      }, t2 + wait - t1)
    }
  }
  fun.cancel = () => {
    clearTimeout(timer)
    timer = null
  }
  // @ts-ignore
  return fun
}
